//This filter is tested and seems to work correctly
//Issue left: could need much optimization

using System;
using System.Drawing;
using System.Drawing.Imaging;

namespace LowLevelGraphics.Filter
{
    /// <summary>
    /// This is crimmins speckle removal. Momentarily totally unoptimized.
    /// Crimmins is an operator that applies the Crimmins Speckle Reduction 
    /// Algorithm.
    /// </summary>
    public class CrimminsSpeckleRemoval : BaseImageFilter
    {
        protected int m_nIterations = 1;
        protected GrayScale m_Grayscale = new GrayScale();

        /// <summary>
        /// Default no-arg constructor.
        /// </summary>
        public CrimminsSpeckleRemoval()
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="CrimminsSpeckleRemoval"/> class.
        /// </summary>
        /// <param name="_nIterations">The _n iterations.</param>
        public CrimminsSpeckleRemoval(int _nIterations)
        {
            this.m_nIterations = 1;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="CrimminsSpeckleRemoval"/> class.
        /// </summary>
        /// <param name="_crimminsSpeckleRemoval">The _crimmins speckle removal.</param>
        internal CrimminsSpeckleRemoval(CrimminsSpeckleRemoval _crimminsSpeckleRemoval)
        {
            this.m_nIterations = _crimminsSpeckleRemoval.m_nIterations;
        }

        /// <summary>
        /// Gets or sets iterations
        /// </summary>
        public int Iterations
        {
            get { return m_nIterations; }
            set { m_nIterations = value; }
        }

        /// <summary>
        /// Execute this Filter
        /// A Debug Statement is written if a concrete Filter is called
        /// </summary>
        /// <param name="_bitmap"></param>
        /// <returns></returns>
        public override UnsafeBitmap Execute(UnsafeBitmap _bitmap)
        {
            ApplyCrimmins(_bitmap, _bitmap.Width, _bitmap.Height, m_nIterations);
            return _bitmap;
        }

        //TODO: Crimminsspeckleremoval: Those Loops can surely be optimized.
        //Caching would be another improvement to speedup
        /// <summary>
        /// * Takes a 2d array of grey level values and applies the
        /// Crimmins Speckle Reduction algorithm to it for a
        /// specified number of iterations.
        /// </summary>
        /// <param name="_image">image the input image</param>
        /// <param name="_nWidth">Width of the _n.</param>
        /// <param name="_nHeight">Height of the _n.</param>
        /// <param name="_nIterations">The _n iterations.</param>
        private void ApplyCrimmins(UnsafeBitmap _image, int _nWidth,
                     int _nHeight, int _nIterations)
        {
            UnsafeBitmap image = _image;
            UnsafeBitmap image2 = (UnsafeBitmap)_image.Clone();
            m_Grayscale.Execute(image);

            int y = 0;
            int x = 0;
            int i = 0;
            int j = 0;
            int its = 0;

            for (its = 0; its < _nIterations; ++its)
            {
                for (y = 1; y < _nHeight; y++)
                {
                    for (x = 1; x < _nWidth; x++)
                    {
                        int nGray = image.GetPixelR(x - 1, y);
                        if (x - 1 >= 0 && nGray > image.GetPixelR(x, y) + 1)
                        {
                            image2.SetGrayPixel(x, y, image.GetPixelR(x, y) + 1);
                        }
                        else
                        {
                            image2.SetPixel(x, y, image.GetPixel(x, y));
                        }
                    }
                }

                for (y = 0; y < _nHeight; ++y)
                {
                    for (x = 0; x < _nWidth; ++x)
                    {
                        if (x - 1 >= 0 && x + 1 < _nWidth
                           && image2.GetPixelR(x - 1, y) > image2.GetPixelR(x, y) && image2.GetPixelR(x + 1, y) >= image2.GetPixelR(x, y)
                        )
                        {
                            image.SetGrayPixel(x, y, image2.GetPixelR(x, y) + 1);
                        }
                        else
                        {
                            image.SetGrayPixel(x, y, image2.GetPixelR(x, y));
                        }
                    }
                }

                for (y = 0; y < _nHeight; ++y)
                {
                    for (x = 0; x < _nWidth; ++x)
                    {
                        if (x + 1 < _nWidth && x - 1 >= 0
                           && image.GetPixelR(x - 1, y) >= image.GetPixelR(x, y) && image.GetPixelR(x + 1, y) > image.GetPixelR(x, y))
                        {
                            image2.SetGrayPixel(x, y, image.GetPixelR(x, y) + 1);
                        }
                        else
                        {
                            image2.SetGrayPixel(x, y, image.GetPixelR(x, y));
                        }
                    }
                }
                
                for (y = 0; y < _nHeight; ++y)
                {
                    for (x = 0; x < _nWidth; ++x)
                    {
                        if (x + 1 < _nWidth && image2.GetPixelR(x + 1, y) > image2.GetPixelR(x, y) + 1)
                        {
                            image.SetGrayPixel(x, y, image2.GetPixelR(x, y) + 1);
                        }
                        else
                        {
                            image.SetGrayPixel(x, y, image2.GetPixelR(x, y));
                        }
                    }
                }
                
                for (y = 0; y < _nHeight; ++y)
                {
                    for (x = 0; x < _nWidth; ++x)
                    {
                        if (x - 1 >= 0 && image.GetPixelR(x - 1, y) < image.GetPixelR(x, y) - 1)
                        {
                            image2.SetGrayPixel(x, y, image.GetPixelR(x, y) - 1);
                        }
                        else
                        {
                            image2.SetGrayPixel(x, y, image.GetPixelR(x, y));
                        }
                    }
                }
                
                for (y = 0; y < _nHeight; ++y)
                {
                    for (x = 0; x < _nWidth; ++x)
                    {
                        if (x - 1 >= 0 && x + 1 < _nWidth
                           && image2.GetPixelR(x - 1, y) < image2.GetPixelR(x, y) && image2.GetPixelR(x + 1, y) <= image2.GetPixelR(x, y))
                        {
                            image.SetGrayPixel(x, y, image2.GetPixelR(x, y) - 1);
                        }
                        else
                        {
                            image.SetGrayPixel(x, y, image2.GetPixelR(x, y));
                        }
                    }
                }

                for (j = 0; j < _nHeight; ++j)
                {
                    for (i = 0; i < _nWidth; ++i)
                    {
                        if (i + 1 < _nWidth && i - 1 >= 0
                           && image.GetPixelR(i - 1, j) <= image.GetPixelR(i, j) && image.GetPixelR(i + 1, j) < image.GetPixelR(i, j))
                        {
                            image2.SetGrayPixel(i, j, image.GetPixelR(i, j) - 1);
                        }
                        else
                        {
                            image2.SetGrayPixel(i, j, image.GetPixelR(i, j));
                        }
                    }
                }

                for (y = 0; y < _nHeight; ++y)
                {
                    for (x = 0; x < _nWidth; ++x)
                    {
                        if (x + 1 < _nWidth && image2.GetPixelR(x + 1, y) < image2.GetPixelR(x, y) - 1)
                        {
                            image.SetGrayPixel(x, y, image2.GetPixelR(x, y) - 1);
                        }
                        else
                        {
                            image.SetGrayPixel(x, y, image2.GetPixelR(x, y));
                        }
                    }
                }

                for (y = 0; y < _nHeight; ++y)
                {
                    for (x = 0; x < _nWidth; ++x)
                    {
                        if (y - 1 >= 0 && image.GetPixelR(x, y - 1) > image.GetPixelR(x, y) + 1)
                        {
                            image2.SetGrayPixel(x, y, image.GetPixelR(x, y) + 1);
                        }
                        else
                        {
                            image2.SetGrayPixel(x, y, image.GetPixelR(x, y));
                        }
                    }
                }

                for (y = 0; y < _nHeight; ++y)
                {
                    for (x = 0; x < _nWidth; ++x)
                    {
                        if (y - 1 >= 0 && y + 1 < _nHeight
                           && image2.GetPixelR(x, y - 1) > image2.GetPixelR(x, y) && image2.GetPixelR(x, y + 1) >= image2.GetPixelR(x, y))
                        {
                            image.SetGrayPixel(x, y, image2.GetPixelR(x, y) + 1);
                        }
                        else
                        {
                            image.SetGrayPixel(x, y, image2.GetPixelR(x, y));
                        }
                    }
                }

                for (y = 0; y < _nHeight; ++y)
                {
                    for (x = 0; x < _nWidth; ++x)
                    {
                        if (y + 1 < _nHeight && y - 1 >= 0
                           && image.GetPixelR(x, y - 1) >= image.GetPixelR(x, y) && image.GetPixelR(x, y + 1) > image.GetPixelR(x, y))
                        {
                            image2.SetGrayPixel(x, y, image.GetPixelR(x, y) + 1);
                        }
                        else
                        {
                            image2.SetGrayPixel(x, y, image.GetPixelR(x, y));
                        }
                    }
                }

                for (y = 0; y < _nHeight; ++y)
                {
                    for (x = 0; x < _nWidth; ++x)
                    {
                        if (y + 1 < _nHeight && image2.GetPixelR(x, y + 1) > image2.GetPixelR(x, y) + 1)
                        {
                            image.SetGrayPixel(x, y, image2.GetPixelR(x, y) + 1);
                        }
                        else
                        {
                            image.SetGrayPixel(x, y, image2.GetPixelR(x, y));
                        }
                    }
                }

                for (y = 0; y < _nHeight; ++y)
                {
                    for (x = 0; x < _nWidth; ++x)
                    {
                        if (y - 1 >= 0 && image.GetPixelR(x, y - 1) < image.GetPixelR(x, y) - 1)
                        {
                            image2.SetGrayPixel(x, y, image.GetPixelR(x, y) - 1);
                        }
                        else
                        {
                            image2.SetGrayPixel(x, y, image.GetPixelR(x, y));
                        }
                    }
                }

                for (y = 0; y < _nHeight; ++y)
                {
                    for (x = 0; x < _nWidth; ++x)
                    {
                        if (y - 1 >= 0 && y + 1 < _nHeight
                           && image2.GetPixelR(x, y - 1) < image2.GetPixelR(x, y) && image2.GetPixelR(x, y + 1) <= image2.GetPixelR(x, y))
                        {
                            image.SetGrayPixel(x, y, image2.GetPixelR(x, y) - 1);
                        }
                        else
                        {
                            image.SetGrayPixel(x, y, image2.GetPixelR(x, y));
                        }
                    }
                }

                for (y = 0; y < _nHeight; ++y)
                {
                    for (x = 0; x < _nWidth; ++x)
                    {
                        if (y + 1 < _nHeight && y - 1 >= 0
                           && image.GetPixelR(x, y - 1) <= image.GetPixelR(x, y) && image.GetPixelR(x, y + 1) < image.GetPixelR(x, y))
                        {
                            image2.SetGrayPixel(x, y, image.GetPixelR(x, y) - 1);
                        }
                        else
                        {
                            image2.SetGrayPixel(x, y, image.GetPixelR(x, y));
                        }
                    }
                }

                for (y = 0; y < _nHeight; ++y)
                {
                    for (x = 0; x < _nWidth; ++x)
                    {
                        if (y + 1 < _nHeight && image2.GetPixelR(x, y + 1) < image2.GetPixelR(x, y) - 1)
                        {
                            image.SetGrayPixel(x, y, image2.GetPixelR(x, y) - 1);
                        }
                        else
                        {
                            image.SetGrayPixel(x, y, image2.GetPixelR(x, y));
                        }
                    }
                }

                for (y = 0; y < _nHeight; ++y)
                {
                    for (x = 0; x < _nWidth; ++x)
                    {
                        if (x + 1 < _nWidth && y - 1 >= 0
                           && image.GetPixelR(x + 1, y - 1) > image.GetPixelR(x, y) + 1)
                        {
                            image2.SetGrayPixel(x, y, image.GetPixelR(x, y) + 1);
                        }
                        else
                        {
                            image2.SetGrayPixel(x, y, image.GetPixelR(x, y));
                        }
                    }
                }

                for (y = 0; y < _nHeight; ++y)
                {
                    for (x = 0; x < _nWidth; ++x)
                    {
                        if (x - 1 >= 0 && x + 1 < _nWidth && y - 1 >= 0 && y + 1 < _nHeight
                           && image2.GetPixelR(x + 1, y - 1) > image2.GetPixelR(x, y)
                           && image2.GetPixelR(x - 1, y + 1) >= image2.GetPixelR(x, y))
                        {
                            image.SetGrayPixel(x, y, image2.GetPixelR(x, y) + 1);
                        }
                        else 
                        {
                            image.SetGrayPixel(x, y, image2.GetPixelR(x, y));
                        }
                    }
                }

                for (y = 0; y < _nHeight; ++y)
                {
                    for (x = 0; x < _nWidth; ++x)
                    {
                        if (x - 1 >= 0 && x + 1 < _nWidth && y - 1 >= 0 && y + 1 < _nHeight
                           && image.GetPixelR(x + 1, y - 1) >= image.GetPixelR(x, y)
                           && image.GetPixelR(x - 1, y + 1) > image.GetPixelR(x, y))
                        {
                            image2.SetGrayPixel(x, y, image.GetPixelR(x, y) + 1);
                        }
                        else
                        {
                            image2.SetGrayPixel(x, y, image.GetPixelR(x, y));
                        }
                    }
                }

                for (j = 0; j < _nHeight; ++j)
                {
                    for (i = 0; i < _nWidth; ++i)
                    {
                        if (i - 1 >= 0 && j + 1 < _nHeight
                           && image2.GetPixelR(i - 1, j + 1) > image2.GetPixelR(i, j) + 1)
                        {
                            image.SetGrayPixel(i, j, image2.GetPixelR(i, j) + 1);
                        }
                        else
                        {
                            image.SetGrayPixel(i, j, image2.GetPixelR(i, j));
                        }
                    }
                }

                for (y = 0; y < _nHeight; ++y)
                {
                    for (x = 0; x < _nWidth; ++x)
                    {
                        if (x + 1 < _nWidth && y - 1 >= 0
                           && image.GetPixelR(x + 1, y - 1) < image.GetPixelR(x, y) - 1)
                        {
                            image2.SetGrayPixel(x, y, image.GetPixelR(x, y) - 1);
                        }
                        else
                        {
                            image2.SetGrayPixel(x, y, image.GetPixelR(x, y));
                        }
                    }
                }

                for (y = 0; y < _nHeight; ++y)
                {
                    for (x = 0; x < _nWidth; ++x)
                    {
                        if (x - 1 >= 0 && x + 1 < _nWidth && y - 1 >= 0 && y + 1 < _nHeight
                           && image2.GetPixelR(x + 1, y - 1) < image2.GetPixelR(x, y)
                           && image2.GetPixelR(x - 1, y + 1) <= image2.GetPixelR(x, y))
                        {
                            image.SetGrayPixel(x, y, image2.GetPixelR(x, y) - 1);
                        }
                        else
                        {
                            image.SetGrayPixel(x, y, image2.GetPixelR(x, y));
                        }
                    }
                }

                for (y = 0; y < _nHeight; ++y)
                {
                    for (x = 0; x < _nWidth; ++x)
                    {
                        if (x - 1 >= 0 && x + 1 < _nWidth && y - 1 >= 0 && y + 1 < _nHeight
                           && image.GetPixelR(x + 1, y - 1) <= image.GetPixelR(x, y)
                           && image.GetPixelR(x - 1, y + 1) < image.GetPixelR(x, y))
                        {
                            image2.SetGrayPixel(x, y, image.GetPixelR(x, y) - 1);
                        }
                        else
                        {
                            image2.SetGrayPixel(x, y, image.GetPixelR(x, y));
                        }
                    }
                }

                for (y = 0; y < _nHeight; ++y)
                {
                    for (x = 0; x < _nWidth; ++x)
                    {
                        if (x - 1 >= 0 && y + 1 < _nHeight
                           && image2.GetPixelR(x - 1, y + 1) < image2.GetPixelR(x, y) - 1)
                        {
                            image.SetGrayPixel(x, y, image2.GetPixelR(x, y) - 1);
                        }
                        else
                        {
                            image.SetGrayPixel(x, y, image2.GetPixelR(x, y));
                        }
                    }
                }

                for (y = 0; y < _nHeight; ++y)
                {
                    for (x = 0; x < _nWidth; ++x)
                    {
                        if (x + 1 < _nWidth && y + 1 < _nHeight
                           && image.GetPixelR(x + 1, y + 1) > image.GetPixelR(x, y) + 1)
                        {
                            image2.SetGrayPixel(x, y, image.GetPixelR(x, y) + 1);
                        }
                        else
                        {
                            image2.SetGrayPixel(x, y, image.GetPixelR(x, y));
                        }
                    }
                }
                
                for (y = 0; y < _nHeight; ++y)
                {
                    for (x = 0; x < _nWidth; ++x)
                    {
                        if (x - 1 >= 0 && x + 1 < _nWidth && y - 1 >= 0 && y + 1 < _nHeight
                           && image2.GetPixelR(x + 1, y + 1) > image2.GetPixelR(x, y)
                           && image2.GetPixelR(x - 1, y - 1) >= image2.GetPixelR(x, y))
                        {
                            image.SetGrayPixel(x, y, image2.GetPixelR(x, y) + 1);
                        }
                        else
                        {
                            image.SetGrayPixel(x, y, image2.GetPixelR(x, y));
                        }
                    }
                }

                for (y = 0; y < _nHeight; ++y)
                {
                    for (x = 0; x < _nWidth; ++x)
                    {
                        if (x - 1 >= 0 && x + 1 < _nWidth && y - 1 >= 0 && y + 1 < _nHeight
                           && image.GetPixelR(x + 1, y + 1) >= image.GetPixelR(x, y)
                           && image.GetPixelR(x - 1, y - 1) > image.GetPixelR(x, y))
                        {
                            image2.SetGrayPixel(x, y, image.GetPixelR(x, y) + 1);
                        }
                        else
                        {
                            image2.SetGrayPixel(x, y, image.GetPixelR(x, y));
                        }
                    }
                }

                for (y = 0; y < _nHeight; ++y)
                {
                    for (x = 0; x < _nWidth; ++x)
                    {
                        if (x - 1 >= 0 && y - 1 >= 0
                           && image2.GetPixelR(x - 1, y - 1) > image2.GetPixelR(x, y) + 1)
                        {
                            image.SetGrayPixel(x, y, image2.GetPixelR(x, y) + 1);
                        }
                        else
                        {
                            image.SetGrayPixel(x, y, image2.GetPixelR(x, y));
                        }
                    }
                }

                for (y = 0; y < _nHeight; ++y)
                {
                    for (x = 0; x < _nWidth; ++x)
                    {
                        if (x + 1 < _nWidth && y + 1 < _nHeight
                           && image.GetPixelR(x + 1, y + 1) < image.GetPixelR(x, y) - 1)
                        {
                            image2.SetGrayPixel(x, y, image.GetPixelR(x, y) - 1);
                        }
                        else
                        {
                            image2.SetGrayPixel(x, y, image.GetPixelR(x, y));
                        }
                    }
                }

                for (j = 0; j < _nHeight; ++j)
                {
                    for (x = 0; x < _nWidth; ++x)
                    {
                        if (x - 1 >= 0 && x + 1 < _nWidth && j - 1 >= 0 && j + 1 < _nHeight
                           && image2.GetPixelR(x + 1, j + 1) < image2.GetPixelR(x, j)
                           && image2.GetPixelR(x - 1, j - 1) <= image2.GetPixelR(x, j))
                        {
                            image.SetGrayPixel(x, j, image2.GetPixelR(x, j) - 1);
                        }
                        else
                        {
                            image.SetGrayPixel(x, j, image2.GetPixelR(x, j));
                        }
                    }
                }

                for (y = 0; y < _nHeight; ++y)
                {
                    for (x = 0; x < _nWidth; ++x)
                    {
                        if (x - 1 >= 0 && x + 1 < _nWidth && y - 1 >= 0 && y + 1 < _nHeight
                           && image.GetPixelR(x + 1, y + 1) <= image.GetPixelR(x, y)
                            && image.GetPixelR(x - 1, y - 1) < image.GetPixelR(x, y))
                        {
                            image2.SetGrayPixel(x, y, image.GetPixelR(x, y) - 1);
                        }
                        else
                        {
                            image2.SetGrayPixel(x, y, image.GetPixelR(x, y));
                        }
                    }
                }

                for (y = 0; y < _nHeight; ++y)
                {
                    for (x = 0; x < _nWidth; ++x)
                    {
                        if (x - 1 >= 0 && y - 1 >= 0
                           && image2.GetPixelR(x - 1, y - 1) < image2.GetPixelR(x, y) - 1)
                        {
                            image.SetGrayPixel(x, y, image2.GetPixelR(x, y) - 1);
                        }
                        else
                        {
                            image.SetGrayPixel(x, y, image2.GetPixelR(x, y));
                        }
                    }
                }
                image2.Dispose();
            }
        }

        /// <summary>
        /// This is used to clone a filter, each filter must implement it's own clone functionality
        /// </summary>
        /// <returns></returns>
        public override object Clone()
        {
            return new CrimminsSpeckleRemoval(this);
        }
    }
}









